package com.mixsmart.security;

import com.mixsmart.exception.NullArgumentException;
import com.mixsmart.utils.StringUtils;
import org.apache.commons.codec.binary.Base64;

import java.nio.charset.StandardCharsets;

/**
 * 加密算法
 * @author 乌草坡
 *
 */
public class SecurityUtils {

	/**
	 * MD5加密
	 * @param value 需要加密内容
	 * @return 返回MD5加密后的值
	 */
	public static String md5(String value) {
		if(StringUtils.isNotEmpty(value)) {
			byte[] bytes;
			try {
				bytes = value.getBytes("UTF-8");
				bytes = Coder.encryptMD5(bytes);
				StringBuilder md5StrBuilder = new StringBuilder();
				for (int i = 0; i < bytes.length; i++) {
					if (Integer.toHexString(0xFF & bytes[i]).length() == 1)
						md5StrBuilder.append("0").append(Integer.toHexString(0xFF & bytes[i]));
					else
						md5StrBuilder.append(Integer.toHexString(0xFF & bytes[i]));
				}
				value = md5StrBuilder.toString();
			} catch (Exception e) {
				throw new SecurityAlgorithmException(e);
			}
		}
		return value;
	}
	
	/**
	 * SHA加密
	 * @param value 需要加密内容
	 * @return 返回加密后的值
	 */
	public static String sha(String value) {
		if(StringUtils.isNotEmpty(value)) {
			byte[] bytes;
			try {
				bytes = value.getBytes(StandardCharsets.UTF_8);
				bytes = Coder.encryptSHA(bytes);
				value = byteArrayToHex(bytes);
			} catch (Exception e) {
				throw new SecurityAlgorithmException(e);
			}
		}
		return value;
	}

	/**
	 * SHA-256加密
	 * @param value 需要加密内容
	 * @return 返回加密后的值
	 */
	public static String sha256(String value) {
		if(StringUtils.isNotEmpty(value)) {
			byte[] bytes;
			try {
				bytes = value.getBytes(StandardCharsets.UTF_8);
				bytes = Coder.encryptSHA256(bytes);
				value = byteArrayToHex(bytes);
			} catch (Exception e) {
				throw new SecurityAlgorithmException(e);
			}
		}
		return value;
	}

	/**
	 * SHA-512加密
	 * @param value 需要加密内容
	 * @return 返回加密后的值
	 */
	public static String sha512(String value) {
		if(StringUtils.isNotEmpty(value)) {
			byte[] bytes;
			try {
				bytes = value.getBytes(StandardCharsets.UTF_8);
				bytes = Coder.encryptSHA512(bytes);
				value = byteArrayToHex(bytes);
			} catch (Exception e) {
				throw new SecurityAlgorithmException(e);
			}
		}
		return value;
	}

	/**
	 * SHA3-256加密
	 * @param value
	 * @return 返回加密后的值
	 */
	/*public static String sha3_256(String value) {
		if(StringUtils.isNotEmpty(value)) {
			byte[] bytes;
			try {
				bytes = value.getBytes(StandardCharsets.UTF_8);
				bytes = Coder.encryptSHA3_256(bytes);
				value = byteArrayToHex(bytes);
			} catch (Exception e) {
				throw new SecurityAlgorithmException(e);
			}
		}
		return value;
	}*/

	/**
	 * SHA3-512加密
	 * @param value
	 * @return 返回加密后的值
	 */
	/*public static String sha3_512(String value) {
		if(StringUtils.isNotEmpty(value)) {
			byte[] bytes;
			try {
				bytes = value.getBytes(StandardCharsets.UTF_8);
				bytes = Coder.encryptSHA3_512(bytes);
				value = byteArrayToHex(bytes);
			} catch (Exception e) {
				throw new SecurityAlgorithmException(e);
			}
		}
		return value;
	}*/
	
	/**
	 * 字符数组转化为十六进制
	 * @param byteArray 需要转换的字符数组
	 * @return 返回十六进制字符串
	 */
	private static String byteArrayToHex(byte[] byteArray) {
		char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F' };
		char[] resultCharArray =new char[byteArray.length * 2];
		int index = 0;
		for(byte b :byteArray){
			resultCharArray[index++] = hexDigits[b>>>4 & 0xf];
			resultCharArray[index++] = hexDigits[b & 0xf];
		}
		return new String(resultCharArray);
	}
	
	/**
	 * DES加密
	 * @param value 需要加密的字符串
	 * @param key 密钥
	 * @return 返回加密后的值
	 */
	public static String desEncode(String value, String key) {
		return desEncode(value, key, false);
	}

	/**
	 * DES加密
	 * @param value 需要加密的字符串
	 * @param key 密钥
	 * @param isKeyBase64 key是否base64编码
	 * @return 返回加密后的值
	 */
	public static String desEncode(String value, String key, boolean isKeyBase64) {
		checkArgs(value, key);
		if(StringUtils.isNotEmpty(value) && StringUtils.isNotEmpty(key)) {
			try {
				byte[] bytes = value.getBytes("UTF-8");
				bytes = DESCoder.encrypt(bytes, key, isKeyBase64);
				value = Base64.encodeBase64String(bytes);
			} catch (Exception e) {
				throw new SecurityAlgorithmException(e);
			}
		}
		return value;
	}
	
	
	/**
	 * DES解密
	 * @param value 需要解密的字符串
	 * @param key 密钥
	 * @return 返回解密后的值
	 */
	public static String desDecode(String value,String key) {
		return desDecode(value, key, false);
	}

	/**
	 * DES解密
	 * @param value 需要解密的字符串
	 * @param key 密钥
	 * @param isKeyBase64 key是否base64编码
	 * @return 返回解密后的值
	 */
	public static String desDecode(String value,String key, boolean isKeyBase64) {
		checkArgs(value, key);
		try {
			byte[] bytes = Base64.decodeBase64(value);
			bytes = DESCoder.decrypt(bytes,key, isKeyBase64);
			value = new String(bytes,"UTF8");
		} catch (Exception e) {
			throw new SecurityAlgorithmException(e);
		}
		return value;
	}

	/**
	 * 3DES加密；
	 * (使用：DESede/ECB/PKCS5Padding）
	 * @param value 需要加密的字符串
	 * @param key 密钥
	 * @return 返回加密后的值
	 */
	public static String tripleEncode(String value, String key) {
		checkArgs(value, key);
		if(StringUtils.isNotEmpty(value)
				&& StringUtils.isNotEmpty(key)) {
			try {
				return TripleDESCoder.encode(value, key);
			} catch (Exception e) {
				throw new SecurityAlgorithmException(e);
			}
		}
		return value;
	}


	/**
	 * 3DES解密；
	 * (使用：DESede/ECB/PKCS5Padding）
	 * @param value 需要解密的字符串
	 * @param key 密钥
	 * @return 返回解密后的值
	 */
	public static String tripleDecode(String value,String key) {
		try {
			TripleDESCoder.decode(value, key);
		} catch (Exception e) {
			throw new SecurityAlgorithmException(e);
		}
		return value;
	}

	/**
	 * AES加密；
	 * （使用：AES/ECB/PKCS5Padding）
	 * @param value 需要加密的内容
	 * @param key 密钥
	 * @return 返回结果
	 */
	public static String aesEncode(String value, String key) {
		checkArgs(value, key);
		try {
			return AESCoder.encrypt(value, key);
		} catch (Exception e) {
			throw new SecurityAlgorithmException(e);
		}
	}

	/**
	 * AES加密；
	 * （使用：AES/GCM/PKCS5Padding）
	 * @param value 需要加密的内容
	 * @param key 密钥
	 * @param ivStr 密文初始向量
	 * @return 返回结果
	 */
	public static String aesGcmEncode(String value, String key, String ivStr) {
		checkArgs(value, key);
		try {
			return AESCoder.encryptGcm(value, key, ivStr);
		} catch (Exception e) {
			throw new SecurityAlgorithmException(e);
		}
	}

	/**
	 * AES加密；
	 * （使用：AES/GCM/PKCS5Padding）
	 * @param value 需要加密的内容
	 * @param key 密钥
	 * @param len 密文长度
	 * @param ivStr 密文初始向量
	 * @return 返回结果
	 */
	public static String aesGcmEncode(String value, String key, int len, String ivStr) {
		checkArgs(value, key);
		try {
			return AESCoder.encryptGcm(value, key, len,ivStr);
		} catch (Exception e) {
			throw new SecurityAlgorithmException(e);
		}
	}

	/**
	 * AES解密；
	 * （使用：AES/ECB/PKCS5Padding）
	 * @param value 需要解密的内容
	 * @param key 密钥
	 * @return 返回结果
	 */
	public static String aesDecode(String value, String key) {
		checkArgs(value, key);
		try {
			return AESCoder.decrypt(value, key);
		} catch (Exception e) {
			throw new SecurityAlgorithmException(e);
		}
	}

	/**
	 * AES/GCM解密；
	 * （使用：AES/GCM/PKCS5Padding）
	 * @param value 需要解密的内容
	 * @param key 密钥
	 * @param ivStr 密文初始向量
	 * @return 返回结果
	 */
	public static String aesGcmDecode(String value, String key, String ivStr) {
		checkArgs(value, key);
		try {
			return AESCoder.decryptGcm(value, key, ivStr);
		} catch (Exception e) {
			throw new SecurityAlgorithmException(e);
		}
	}

	/**
	 * AES/GCM解密；
	 * （使用：AES/GCM/PKCS5Padding）
	 * @param value 需要解密的内容
	 * @param key 密钥
	 * @param len 密文长度
	 * @param ivStr 密文初始向量
	 * @return 返回结果
	 */
	public static String aesGcmDecode(String value, String key, int len, String ivStr) {
		checkArgs(value, key);
		try {
			return AESCoder.decryptGcm(value, key, len, ivStr);
		} catch (Exception e) {
			throw new SecurityAlgorithmException(e);
		}
	}

	/**
	 * 获取AES KEY（自动生成KEY）
	 * @return 密钥
	 */
	public static String getAesKey() {
		try {
			return AESCoder.getSecretKey();
		} catch (Exception ex) {
			throw new SecurityAlgorithmException(ex);
		}
	}

	/**
	 * 通过seed参数生成AES KEY
	 * @param seed 秘钥种子
	 * @return 密钥
	 */
	public static String getAesKeyBySeed(String seed) {
		try {
			return AESCoder.getSecretKey(seed);
		} catch (Exception ex) {
			throw new SecurityAlgorithmException(ex);
		}
	}

	/**
	 * HmacMD5加密
	 * @param value 需要加密的内容
	 * @param key 密钥
	 * @return 密文
	 */
	public static String encryptHmacMd5(String value, String key) {
		checkArgs(value, key);
		try {
			byte[] encryptArray = Coder.encryptHMAC(value.getBytes(StandardCharsets.UTF_8), key);
			return Base64.encodeBase64String(encryptArray);
		} catch (Exception ex) {
			throw new SecurityAlgorithmException(ex);
		}
	}

	/**
	 * HmacSHA1加密
	 * @param value 需要加密的内容
	 * @param key 密钥
	 * @return 密文
	 */
	public static String encryptHmacSha1(String value, String key) {
		checkArgs(value, key);
		try {
			byte[] encryptArray = Coder.encryptHmacSHA1(value.getBytes(StandardCharsets.UTF_8), key);
			return Base64.encodeBase64String(encryptArray);
		} catch (Exception ex) {
			throw new SecurityAlgorithmException(ex);
		}
	}

	/**
	 * HmacSHA256加密
	 * @param value 需要加密的内容
	 * @param key 密钥
	 * @return 密文
	 */
	public static String encryptHmacSha256(String value, String key) {
		checkArgs(value, key);
		try {
			byte[] encryptArray = Coder.encryptHmacSHA256(value.getBytes(StandardCharsets.UTF_8), key);
			return Base64.encodeBase64String(encryptArray);
		} catch (Exception ex) {
			throw new SecurityAlgorithmException(ex);
		}
	}

	/**
	 * HmacSHA512加密
	 * @param value 需要加密的内容
	 * @param key 密钥
	 * @return 密文
	 */
	public static String encryptHmacSha512(String value, String key) {
		checkArgs(value, key);
		try {
			byte[] encryptArray = Coder.encryptHmacSHA512(value.getBytes(StandardCharsets.UTF_8), key);
			return Base64.encodeBase64String(encryptArray);
		} catch (Exception ex) {
			throw new SecurityAlgorithmException(ex);
		}
	}

	private static void checkArgs(String value, String key) {
		if(StringUtils.isEmpty(value)) {
			throw new NullArgumentException("加密内容不能为空");
		}
		if(StringUtils.isEmpty(key)) {
			throw new NullArgumentException("密钥不能为空");
		}
	}

	/**
	 * Base64加密
	 * @param value 需要加密的内容
	 * @return 返回加密后的结果
	 */
	public static String encodeBase64(String value) {
		if(StringUtils.isNotEmpty(value)) {
			return java.util.Base64.getEncoder().encodeToString(value.getBytes(StandardCharsets.UTF_8));
		} else {
			return null;
		}
	}

	/**
	 * Base64解密
	 * @param value 需要解密的内容
	 * @return 返回解密后的结果
	 */
	public static String decodeBase64(String value) {
		if(StringUtils.isNotEmpty(value)) {
			return new String(java.util.Base64.getDecoder().decode(value.getBytes(StandardCharsets.UTF_8)));
		} else {
			return null;
		}
	}
}
